package org.example.mq.mqclient;

import org.example.mq.common.BasicReturns;
import org.example.mq.common.Request;
import org.example.mq.common.Response;
import org.example.mq.common.SubscribeReturns;
import org.example.mq.common.exception.MqException;
import org.example.mq.common.tools.BinaryTool;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Connection {
    private final String virtualHost;
    private Socket socket = null;
    //<channelId,channel>
    private ConcurrentHashMap<String,Channel> channelMap = new ConcurrentHashMap<>();

    private InputStream inputStream;
    private OutputStream outputStream;
    private DataOutputStream dataOutputStream;
    private DataInputStream dataInputStream;

    //执行消息处理回调函数
    private ExecutorService callbackPool = null;

    public Connection(String host, int port, String virtualHost) throws IOException {
        this.socket = new Socket(host,port);
        this.virtualHost = virtualHost;

        callbackPool = Executors.newFixedThreadPool(4);

        inputStream = socket.getInputStream();
        outputStream = socket.getOutputStream();
        dataInputStream = new DataInputStream(inputStream);
        dataOutputStream = new DataOutputStream(outputStream);

        //创建一个扫描响应线程
        Thread thread = new Thread(()->{
            try {
                while(!socket.isClosed()) {
                    Response response = readResponse();
                    dispatchResponse(response);
                }
            }catch (EOFException | SocketException e) {
                // 这是正常的：socket 被关闭
                System.out.println("[Connection]"+socket.getInetAddress().toString()+" socket closed, listener thread exiting.");
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        });
        thread.start();
    }

    public void close() throws IOException {
        //逐个释放channel
//        System.out.println("channelMap连接数量:"+channelMap.size());
        for (Channel channel : channelMap.values()){
            if(!channel.isClose()){
                channel.close();
            }
        }
        channelMap.clear();

        callbackPool.shutdown();
        inputStream.close();
        outputStream.close();
        socket.close();
    }

    private void dispatchResponse(Response response){
        if(response.getType() == 0xd){
            SubscribeReturns subscribeReturns = (SubscribeReturns) BinaryTool.toObject(response.getPayload());

            Channel channel = channelMap.get(subscribeReturns.getChannelId());
            callbackPool.submit(()->{
                try {
                    channel.getConsumer().handleDelivery(subscribeReturns.getConsumeTag(), subscribeReturns.getBasicProperties(),
                            subscribeReturns.getBody());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
        else{
            BasicReturns basicReturns = (BasicReturns) BinaryTool.toObject(response.getPayload());

            Channel channel = channelMap.get(basicReturns.getChannelId());

            channel.putReturns(basicReturns);
        }
    }

    //发送请求
    public void writeRequest(Request request) throws IOException {
        dataOutputStream.writeInt(request.getType());
        dataOutputStream.writeInt(request.getLength());
        dataOutputStream.write(request.getPayload());
        dataOutputStream.flush();

        System.out.println("[Connection]writeRequest(request)：type"+request.getType()+",length:"+request.getLength());
    }

    //接收响应
    public Response readResponse() throws IOException {
        Response response =  new Response();
        response.setType(dataInputStream.readInt());
        response.setLength(dataInputStream.readInt());
        System.out.println("[Connection]readResponse:length("+response.getLength()+")");

        byte[] payload = new byte[response.getLength()];
        dataInputStream.read(payload);

        if(payload.length!=response.getLength()){
            throw new MqException("[Connection]readResponse:数据不完整！");
        }
        response.setPayload(payload);

        System.out.println("Response：Type："+response.getType()+",length"+response.getLength());
        return response;
    }

    //创建channel 指定channelId
    public Channel createChannel() throws IOException {
        String channelId = "C-" + UUID.randomUUID().toString();
        Channel channel = new Channel(channelId,this);
        channelMap.put(channelId,channel);

        boolean ok = channel.createChannel();

        if(!ok){
            channelMap.remove(channelId);
            //throw new MqException("[Connection]createChannel失败!");
            return null;
        }

        return channel;
    }

    public String getVirtualHost() {
        return virtualHost;
    }
}
